package com.ccp.dev.workflow.service;

import com.ccp.dev.core.util.StringPool;
import com.ccp.dev.workflow.dao.ActGeByteArrayDao;
import com.ccp.dev.workflow.model.ActDefModel;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.impl.persistence.entity.ByteArrayEntityImpl;
import org.flowable.ui.common.service.exception.BaseModelerRestException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * ClassDescribe:
 * 流程xml文件生成服务类
 * @author :wangcheng
 * Date: 2019-06-05
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class BpmnXmlService {
    private static final Logger LOGGER = LoggerFactory.getLogger(BpmnXmlService.class);

    @Resource
    private ActDefModelService actDefModelService;
    @Resource
    private ActGeByteArrayDao actGeByteArrayDao;

    private BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();

    /**
     * 生成bpmn xml文件
     * @param model 模型
     */
    public String generateBpmn20Xml(ActDefModel model) {
        String deployId = model.getDeployId();
        String name = model.getName()+StringPool.SUFFIX_BPMN;
        ByteArrayEntityImpl byteArrayEntity = actGeByteArrayDao.getByteArrayEntityByNameAndDeploymentId(name, deployId);
        String bpmnXml = new String(byteArrayEntity.getBytes(), StandardCharsets.UTF_8);
        if (bpmnXml
                .startsWith(StringPool.XML_HEADER_UTF_8)) {
            bpmnXml = bpmnXml.replace(
                    StringPool.XML_HEADER_UTF_8, "");
        }
        if (bpmnXml.startsWith(StringPool.XML_HEADER_GBK)) {
            bpmnXml = bpmnXml.replace(
                    StringPool.XML_HEADER_GBK, "");
        }
        bpmnXml = bpmnXml.trim();

        return bpmnXml;

    }

    /**
     * 通过bpmnModel获取二进制数据
     * @param bpmnModel bpmn模型对象
     * @return 二进制数据
     */
    public byte[] getBpmnXML(BpmnModel bpmnModel) {
        for (Process process : bpmnModel.getProcesses()) {
            if (StringUtils.isNotEmpty(process.getId())) {
                char firstCharacter = process.getId().charAt(0);
                // no digit is allowed as first character
                if (Character.isDigit(firstCharacter)) {
                    process.setId("a" + process.getId());
                }
            }
        }
        return bpmnXMLConverter.convertToXML(bpmnModel);
    }

    /**
     * 生成bpmn xml文件
     * @param response http响应
     * @param model 模型
     */
    public void generateBpmn20Xml(HttpServletResponse response, ActDefModel model) {
        String name = model.getName().replaceAll(" ", "_") + StringPool.SUFFIX_BPMN20;
        String encodedName = null;
        try {
            encodedName = "UTF-8''" + URLEncoder.encode(name, "UTF-8");
        } catch (Exception e) {
            LOGGER.warn("Failed to encode name " + name);
        }

        String contentDispositionValue = "attachment; filename=" + name;
        if (encodedName != null) {
            contentDispositionValue += "; filename*=" + encodedName;
        }

        response.setHeader("Content-Disposition", contentDispositionValue);
        if (model.getModelEditorJson() != null) {
            try {
                ServletOutputStream servletOutputStream = response.getOutputStream();
                response.setContentType("application/xml");

                BpmnModel bpmnModel = actDefModelService.getBpmnModel(model);
                byte[] xmlBytes = getBpmnXML(bpmnModel);
                BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));

                byte[] buffer = new byte[8096];
                while (true) {
                    int count = in.read(buffer);
                    if (count == -1) {
                        break;
                    }
                    servletOutputStream.write(buffer, 0, count);
                }

                // Flush and close stream
                servletOutputStream.flush();
                servletOutputStream.close();

            } catch (BaseModelerRestException e) {
                throw e;

            } catch (Exception e) {
                LOGGER.error("Could not generate BPMN 2.0 XML", e);
                throw new InternalServerErrorException("Could not generate BPMN 2.0 xml");
            }
        }
    }
}
